// Serialize Stream
// (c) jimon game studio

#ifndef JEH_ISerializeSTREAM
#define JEH_ISerializeSTREAM

#include "jeUnknown.h"
#include "IBuffer.h"
#include "jeStl.h"

namespace je
{
	namespace core
	{
		//! Serialize Stream Mode
		enum SerializeStreamMode
		{
			//! Save Mode
			SSM_SAVE,

			//! Load Mode
			SSM_LOAD
		};

		//! Serialize Stream
		class ISerializeStream:public je::jeUnknown
		{
		protected:
			u1 CurrentLinearBlockMode;
			u32 CurrentLinearBlockSize;
			void * CurrentLinearBlockPointer;
		public:
			//! Constructor
			ISerializeStream()
				:CurrentLinearBlockMode(false),CurrentLinearBlockSize(0),CurrentLinearBlockPointer(NULL)
			{
			}

			//! Destructor
			virtual ~ISerializeStream()
			{
			}

			//! Mapping Hash
			typedef u32 MappingHashType;

			//! Set Mode
			virtual void SetMode(SerializeStreamMode StreamMode) = 0;

			//! Get Mode
			virtual SerializeStreamMode GetMode() = 0;

			//! Set Linear Mode
			virtual void SetLinearMode(u1 Enabled) = 0;

			//! Get Linear Mode
			virtual u1 GetLinearMode() = 0;

			//! New Section
			virtual void NewSection(const MappingHashType & Hash) = 0;

			//! Next Section
			virtual u1 NextSection() = 0;

			//! Open Section
			virtual void OpenSection(const MappingHashType & Hash) = 0;

			//! Close Section
			virtual void CloseSection() = 0;

			//! Work With Value
			virtual u1 WorkWithValue(const MappingHashType & Hash,void * Value,u32 Size) = 0;

			//! Work With Value
			virtual u1 WorkWithValue(const MappingHashType & Hash,void ** Value,u32 * Size) = 0;

			//! Load from buffer
			virtual void LoadBuffer(IBuffer * Buffer) = 0;

			//! Save to buffer
			virtual void SaveBuffer(IBuffer * Buffer) = 0;

			//! Flush Linear Reading
			virtual void FlushLinearReading() = 0;

			//! Begin Linear Block
			virtual void BeginLinearBlock()
			{
				JEASSERT(!CurrentLinearBlockMode)
				CurrentLinearBlockMode = true;
				CurrentLinearBlockSize = 0;
				CurrentLinearBlockPointer = NULL;
			}

			//! End Linear Block
			virtual u1 EndLinearBlock(const MappingHashType & Hash)
			{
				JEASSERT(CurrentLinearBlockMode)
				CurrentLinearBlockMode = false;
				u1 Result = WorkWithValue(Hash,CurrentLinearBlockPointer,CurrentLinearBlockSize);
				if(!Result)
					memset(CurrentLinearBlockPointer,0,CurrentLinearBlockSize);
				return Result;
			}

			//! OnValue method define
			#define JE_SS_ONVALUE(__Type,__Default) \
			void OnValue(const MappingHashType & Hash,__Type * Value,const __Type & Default __Default)\
			{\
				if(CurrentLinearBlockMode)\
				{\
					if(!CurrentLinearBlockPointer)\
						CurrentLinearBlockPointer = Value;\
					CurrentLinearBlockSize += sizeof(__Type);\
					return;\
				}\
				\
				if(!WorkWithValue(Hash,Value,sizeof(__Type)))\
					*Value = Default;\
			}

			//! OnValue methods for standart types
			JE_SS_ONVALUE(u1,=false)
			JE_SS_ONVALUE(c8,=0)
			JE_SS_ONVALUE(s8,=0)
			JE_SS_ONVALUE(u8,=0)
			JE_SS_ONVALUE(s16,=0)
			JE_SS_ONVALUE(u16,=0)
			JE_SS_ONVALUE(s32,=0)
			JE_SS_ONVALUE(u32,=0)
			JE_SS_ONVALUE(f32,=0)
			JE_SS_ONVALUE(f64,=0)
			JE_SS_ONVALUE(je_wchar_t,=0)
			JE_SS_ONVALUE(jeVector2df,=jeVector2df())
			JE_SS_ONVALUE(jeVector2di,=jeVector2di())
			JE_SS_ONVALUE(jeVector3df,=jeVector3df())
			JE_SS_ONVALUE(jeVector3di,=jeVector3di())
			JE_SS_ONVALUE(jeMatrix4f,=jeMatrix4f())

			//! OnValue method for strings
			template<typename T>
			void OnValue(const MappingHashType & Hash,jeString<T> * Value,const jeString<T> & Default = "")
			{
				JEASSERT(!CurrentLinearBlockMode)
				if(GetMode() == SSM_LOAD)
				{
					void * ValuePtr = NULL;
					u32 Size = 0;
					if(WorkWithValue(Hash,&ValuePtr,&Size))
						Value->GetCharsArray().CopyFrom(ValuePtr,Size);
					else
						*Value = Default;
				}
				else
					WorkWithValue(Hash,Value->c_str(),(Value->GetSize()+1) * sizeof(T));
			}
		};

		//#define JE_SS_NEW(__Name) SerializeStream->NewSection(#__Name);
		//#define JE_SS_NEXT (SerializeStream->NextSection())
		//#define JE_SS_BEGIN(__Name) SerializeStream->OpenSection(#__Name);
		//#define JE_SS_END(__Name) SerializeStream->CloseSection();
		//#define JE_SS(__ValueName) SerializeStream->OnValue(#__ValueName,&__ValueName);
		//#define JE_SS_SIZE(__ValueName,__Size) SerializeStream->WorkWithValue(#__ValueName,&__ValueName,__Size);
		//#define JE_SS_ENUM(__ValueName,__ValueType) SerializeStream->WorkWithValue(#__ValueName,&__ValueName,sizeof(__ValueType));

		//#define JE_SS_LINEAR_BLOCK_BEGIN(__Name) SerializeStream->BeginLinearBlock();
		//#define JE_SS_LINEAR_BLOCK_END(__Name) SerializeStream->EndLinearBlock(#__Name);

		//#define JE_SS_LOAD (SerializeStream->GetMode() == core::SSM_LOAD)
		//#define JE_SS_SAVE (SerializeStream->GetMode() == core::SSM_SAVE)

		#define JE_SS_NEW(__Name) SerializeStream->NewSection(0);
		#define JE_SS_NEXT (SerializeStream->NextSection())
		#define JE_SS_BEGIN(__Name) SerializeStream->OpenSection(0);
		#define JE_SS_END(__Name) SerializeStream->CloseSection();
		#define JE_SS(__ValueName) SerializeStream->OnValue(0,&__ValueName);
		#define JE_SS_SIZE(__ValueName,__Size) SerializeStream->WorkWithValue(0,&__ValueName,__Size);
		#define JE_SS_ENUM(__ValueName,__ValueType) SerializeStream->WorkWithValue(0,&__ValueName,sizeof(__ValueType));

		#define JE_SS_LINEAR_BLOCK_BEGIN(__Name) SerializeStream->BeginLinearBlock();
		#define JE_SS_LINEAR_BLOCK_END(__Name) SerializeStream->EndLinearBlock(0);

		#define JE_SS_LOAD (SerializeStream->GetMode() == core::SSM_LOAD)
		#define JE_SS_SAVE (SerializeStream->GetMode() == core::SSM_SAVE)

		//! Create Serialize Stream
		JE_ENGINE_FUNCTION_API ISerializeStream * JE_ENGINE_FUNCTION_CALL CreateSerializeStream();
	}
}

#endif
